/**
 * Copyright (c) 2023 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "plugins/ets/tests/mock/mock_test_helper.h"

#include "runtime/include/runtime.h"
#include "runtime/include/runtime_options.h"
#include "plugins/ets/runtime/types/ets_method.h"

// NOLINTBEGIN(modernize-avoid-c-arrays, readability-magic-numbers)

namespace panda::ets::test {

class EtsNativeInterfaceArrayTest : public MockEtsNapiTestBaseClass {};

TEST_F(EtsNativeInterfaceArrayTest, GetBoolArrayRegionErrorTests)
{
    ets_boolean buf[10] = {0};
    ets_booleanArray arr = env_->NewBooleanArray(5);
    EXPECT_DEATH(env_->GetBooleanArrayRegion(arr, 0, 1, nullptr), "");

    ets_class error_class = env_->FindClass("std/core/ArrayIndexOutOfBoundsException");
    ASSERT_NE(error_class, nullptr);

    {
        env_->GetBooleanArrayRegion(arr, -1, 2, buf);

        ets_error error = env_->ErrorOccurred();
        ASSERT_NE(error, nullptr);
        env_->ErrorClear();
        ASSERT_EQ(env_->IsInstanceOf(static_cast<ets_object>(error), error_class), ETS_TRUE);
    }
    {
        env_->GetBooleanArrayRegion(arr, 0, -2, buf);

        ets_error error = env_->ErrorOccurred();
        ASSERT_NE(error, nullptr);
        env_->ErrorClear();
        ASSERT_EQ(env_->IsInstanceOf(static_cast<ets_object>(error), error_class), ETS_TRUE);
    }
    {
        env_->GetBooleanArrayRegion(arr, 2, 9, buf);

        ets_error error = env_->ErrorOccurred();
        ASSERT_NE(error, nullptr);
        env_->ErrorClear();
        ASSERT_EQ(env_->IsInstanceOf(static_cast<ets_object>(error), error_class), ETS_TRUE);
    }
}

TEST_F(EtsNativeInterfaceArrayTest, GetByteArrayRegionErrorTests)
{
    ets_byte buf[10] = {0};
    ets_byteArray arr = env_->NewByteArray(5);
    EXPECT_DEATH(env_->GetByteArrayRegion(arr, 0, 1, nullptr), "");

    ets_class error_class = env_->FindClass("std/core/ArrayIndexOutOfBoundsException");
    ASSERT_NE(error_class, nullptr);

    {
        env_->GetByteArrayRegion(arr, -1, 2, buf);

        ets_error error = env_->ErrorOccurred();
        ASSERT_NE(error, nullptr);
        env_->ErrorClear();
        ASSERT_EQ(env_->IsInstanceOf(static_cast<ets_object>(error), error_class), ETS_TRUE);
    }
    {
        env_->GetByteArrayRegion(arr, 0, -2, buf);

        ets_error error = env_->ErrorOccurred();
        ASSERT_NE(error, nullptr);
        env_->ErrorClear();
        ASSERT_EQ(env_->IsInstanceOf(static_cast<ets_object>(error), error_class), ETS_TRUE);
    }
    {
        env_->GetByteArrayRegion(arr, 2, 9, buf);

        ets_error error = env_->ErrorOccurred();
        ASSERT_NE(error, nullptr);
        env_->ErrorClear();
        ASSERT_EQ(env_->IsInstanceOf(static_cast<ets_object>(error), error_class), ETS_TRUE);
    }
}

TEST_F(EtsNativeInterfaceArrayTest, GetCharArrayRegionErrorTests)
{
    ets_char buf[10] = {0};
    ets_charArray arr = env_->NewCharArray(5);
    EXPECT_DEATH(env_->GetCharArrayRegion(arr, 0, 1, nullptr), "");

    ets_class error_class = env_->FindClass("std/core/ArrayIndexOutOfBoundsException");
    ASSERT_NE(error_class, nullptr);

    {
        env_->GetCharArrayRegion(arr, -1, 2, buf);

        ets_error error = env_->ErrorOccurred();
        ASSERT_NE(error, nullptr);
        env_->ErrorClear();
        ASSERT_EQ(env_->IsInstanceOf(static_cast<ets_object>(error), error_class), ETS_TRUE);
    }
    {
        env_->GetCharArrayRegion(arr, 0, -2, buf);

        ets_error error = env_->ErrorOccurred();
        ASSERT_NE(error, nullptr);
        env_->ErrorClear();
        ASSERT_EQ(env_->IsInstanceOf(static_cast<ets_object>(error), error_class), ETS_TRUE);
    }
    {
        env_->GetCharArrayRegion(arr, 2, 9, buf);

        ets_error error = env_->ErrorOccurred();
        ASSERT_NE(error, nullptr);
        env_->ErrorClear();
        ASSERT_EQ(env_->IsInstanceOf(static_cast<ets_object>(error), error_class), ETS_TRUE);
    }
}

TEST_F(EtsNativeInterfaceArrayTest, GetShortArrayRegionErrorTests)
{
    ets_short buf[10] = {0};
    ets_shortArray arr = env_->NewShortArray(5);
    EXPECT_DEATH(env_->GetShortArrayRegion(arr, 0, 1, nullptr), "");

    ets_class error_class = env_->FindClass("std/core/ArrayIndexOutOfBoundsException");
    ASSERT_NE(error_class, nullptr);

    {
        env_->GetShortArrayRegion(arr, -1, 2, buf);

        ets_error error = env_->ErrorOccurred();
        ASSERT_NE(error, nullptr);
        env_->ErrorClear();
        ASSERT_EQ(env_->IsInstanceOf(static_cast<ets_object>(error), error_class), ETS_TRUE);
    }
    {
        env_->GetShortArrayRegion(arr, 0, -2, buf);

        ets_error error = env_->ErrorOccurred();
        ASSERT_NE(error, nullptr);
        env_->ErrorClear();
        ASSERT_EQ(env_->IsInstanceOf(static_cast<ets_object>(error), error_class), ETS_TRUE);
    }
    {
        env_->GetShortArrayRegion(arr, 2, 9, buf);

        ets_error error = env_->ErrorOccurred();
        ASSERT_NE(error, nullptr);
        env_->ErrorClear();
        ASSERT_EQ(env_->IsInstanceOf(static_cast<ets_object>(error), error_class), ETS_TRUE);
    }
}

TEST_F(EtsNativeInterfaceArrayTest, GetIntArrayRegionErrorTests)
{
    ets_int buf[10] = {0};
    ets_intArray arr = env_->NewIntArray(5);
    EXPECT_DEATH(env_->GetIntArrayRegion(arr, 0, 1, nullptr), "");

    ets_class error_class = env_->FindClass("std/core/ArrayIndexOutOfBoundsException");
    ASSERT_NE(error_class, nullptr);

    {
        env_->GetIntArrayRegion(arr, -1, 2, buf);

        ets_error error = env_->ErrorOccurred();
        ASSERT_NE(error, nullptr);
        env_->ErrorClear();
        ASSERT_EQ(env_->IsInstanceOf(static_cast<ets_object>(error), error_class), ETS_TRUE);
    }
    {
        env_->GetIntArrayRegion(arr, 0, -2, buf);

        ets_error error = env_->ErrorOccurred();
        ASSERT_NE(error, nullptr);
        env_->ErrorClear();
        ASSERT_EQ(env_->IsInstanceOf(static_cast<ets_object>(error), error_class), ETS_TRUE);
    }
    {
        env_->GetIntArrayRegion(arr, 2, 9, buf);

        ets_error error = env_->ErrorOccurred();
        ASSERT_NE(error, nullptr);
        env_->ErrorClear();
        ASSERT_EQ(env_->IsInstanceOf(static_cast<ets_object>(error), error_class), ETS_TRUE);
    }
}

TEST_F(EtsNativeInterfaceArrayTest, GetLongArrayRegionErrorTests)
{
    ets_long buf[10] = {0};
    ets_longArray arr = env_->NewLongArray(5);
    EXPECT_DEATH(env_->GetLongArrayRegion(arr, 0, 1, nullptr), "");

    ets_class error_class = env_->FindClass("std/core/ArrayIndexOutOfBoundsException");
    ASSERT_NE(error_class, nullptr);

    {
        env_->GetLongArrayRegion(arr, -1, 2, buf);

        ets_error error = env_->ErrorOccurred();
        ASSERT_NE(error, nullptr);
        env_->ErrorClear();
        ASSERT_EQ(env_->IsInstanceOf(static_cast<ets_object>(error), error_class), ETS_TRUE);
    }
    {
        env_->GetLongArrayRegion(arr, 0, -2, buf);

        ets_error error = env_->ErrorOccurred();
        ASSERT_NE(error, nullptr);
        env_->ErrorClear();
        ASSERT_EQ(env_->IsInstanceOf(static_cast<ets_object>(error), error_class), ETS_TRUE);
    }
    {
        env_->GetLongArrayRegion(arr, 2, 9, buf);

        ets_error error = env_->ErrorOccurred();
        ASSERT_NE(error, nullptr);
        env_->ErrorClear();
        ASSERT_EQ(env_->IsInstanceOf(static_cast<ets_object>(error), error_class), ETS_TRUE);
    }
}

TEST_F(EtsNativeInterfaceArrayTest, GetFloatArrayRegionErrorTests)
{
    ets_float buf[10] = {0};
    ets_floatArray arr = env_->NewFloatArray(5);
    EXPECT_DEATH(env_->GetFloatArrayRegion(arr, 0, 1, nullptr), "");

    ets_class error_class = env_->FindClass("std/core/ArrayIndexOutOfBoundsException");
    ASSERT_NE(error_class, nullptr);

    {
        env_->GetFloatArrayRegion(arr, -1, 2, buf);

        ets_error error = env_->ErrorOccurred();
        ASSERT_NE(error, nullptr);
        env_->ErrorClear();
        ASSERT_EQ(env_->IsInstanceOf(static_cast<ets_object>(error), error_class), ETS_TRUE);
    }
    {
        env_->GetFloatArrayRegion(arr, 0, -2, buf);

        ets_error error = env_->ErrorOccurred();
        ASSERT_NE(error, nullptr);
        env_->ErrorClear();
        ASSERT_EQ(env_->IsInstanceOf(static_cast<ets_object>(error), error_class), ETS_TRUE);
    }
    {
        env_->GetFloatArrayRegion(arr, 2, 9, buf);

        ets_error error = env_->ErrorOccurred();
        ASSERT_NE(error, nullptr);
        env_->ErrorClear();
        ASSERT_EQ(env_->IsInstanceOf(static_cast<ets_object>(error), error_class), ETS_TRUE);
    }
}

TEST_F(EtsNativeInterfaceArrayTest, GetDoubleArrayRegionErrorTests)
{
    ets_double buf[10] = {0};
    ets_doubleArray arr = env_->NewDoubleArray(5);
    EXPECT_DEATH(env_->GetDoubleArrayRegion(arr, 0, 1, nullptr), "");

    ets_class error_class = env_->FindClass("std/core/ArrayIndexOutOfBoundsException");
    ASSERT_NE(error_class, nullptr);

    {
        env_->GetDoubleArrayRegion(arr, -1, 2, buf);

        ets_error error = env_->ErrorOccurred();
        ASSERT_NE(error, nullptr);
        env_->ErrorClear();
        ASSERT_EQ(env_->IsInstanceOf(static_cast<ets_object>(error), error_class), ETS_TRUE);
    }
    {
        env_->GetDoubleArrayRegion(arr, 0, -2, buf);

        ets_error error = env_->ErrorOccurred();
        ASSERT_NE(error, nullptr);
        env_->ErrorClear();
        ASSERT_EQ(env_->IsInstanceOf(static_cast<ets_object>(error), error_class), ETS_TRUE);
    }
    {
        env_->GetDoubleArrayRegion(arr, 2, 9, buf);

        ets_error error = env_->ErrorOccurred();
        ASSERT_NE(error, nullptr);
        env_->ErrorClear();
        ASSERT_EQ(env_->IsInstanceOf(static_cast<ets_object>(error), error_class), ETS_TRUE);
    }
}

TEST_F(EtsNativeInterfaceArrayTest, SetBoolArrayRegionErrorTests)
{
    ets_boolean buf[10] = {0};
    ets_booleanArray arr = env_->NewBooleanArray(5);

    ets_class error_class = env_->FindClass("std/core/ArrayIndexOutOfBoundsException");
    ASSERT_NE(error_class, nullptr);

    {
        env_->SetBooleanArrayRegion(arr, -1, 2, buf);

        ets_error error = env_->ErrorOccurred();
        ASSERT_NE(error, nullptr);
        env_->ErrorClear();
        ASSERT_EQ(env_->IsInstanceOf(static_cast<ets_object>(error), error_class), ETS_TRUE);
    }
}

TEST_F(EtsNativeInterfaceArrayTest, SetByteArrayRegionErrorTests)
{
    ets_byte buf[10] = {0};
    ets_byteArray arr = env_->NewByteArray(5);

    ets_class error_class = env_->FindClass("std/core/ArrayIndexOutOfBoundsException");
    ASSERT_NE(error_class, nullptr);

    {
        env_->SetByteArrayRegion(arr, -1, 2, buf);

        ets_error error = env_->ErrorOccurred();
        ASSERT_NE(error, nullptr);
        env_->ErrorClear();
        ASSERT_EQ(env_->IsInstanceOf(static_cast<ets_object>(error), error_class), ETS_TRUE);
    }
}

TEST_F(EtsNativeInterfaceArrayTest, SetCharArrayRegionErrorTests)
{
    ets_char buf[10] = {0};
    ets_charArray arr = env_->NewCharArray(5);

    ets_class error_class = env_->FindClass("std/core/ArrayIndexOutOfBoundsException");
    ASSERT_NE(error_class, nullptr);

    {
        env_->SetCharArrayRegion(arr, -1, 2, buf);

        ets_error error = env_->ErrorOccurred();
        ASSERT_NE(error, nullptr);
        env_->ErrorClear();
        ASSERT_EQ(env_->IsInstanceOf(static_cast<ets_object>(error), error_class), ETS_TRUE);
    }
}

TEST_F(EtsNativeInterfaceArrayTest, SetShortArrayRegionErrorTests)
{
    ets_short buf[10] = {0};
    ets_shortArray arr = env_->NewShortArray(5);

    ets_class error_class = env_->FindClass("std/core/ArrayIndexOutOfBoundsException");
    ASSERT_NE(error_class, nullptr);

    {
        env_->SetShortArrayRegion(arr, -1, 2, buf);

        ets_error error = env_->ErrorOccurred();
        ASSERT_NE(error, nullptr);
        env_->ErrorClear();
        ASSERT_EQ(env_->IsInstanceOf(static_cast<ets_object>(error), error_class), ETS_TRUE);
    }
}

TEST_F(EtsNativeInterfaceArrayTest, SetIntArrayRegionErrorTests)
{
    ets_int buf[10] = {0};
    ets_intArray arr = env_->NewIntArray(5);

    ets_class error_class = env_->FindClass("std/core/ArrayIndexOutOfBoundsException");
    ASSERT_NE(error_class, nullptr);

    {
        env_->SetIntArrayRegion(arr, -1, 2, buf);

        ets_error error = env_->ErrorOccurred();
        ASSERT_NE(error, nullptr);
        env_->ErrorClear();
        ASSERT_EQ(env_->IsInstanceOf(static_cast<ets_object>(error), error_class), ETS_TRUE);
    }
}

TEST_F(EtsNativeInterfaceArrayTest, SetLongArrayRegionErrorTests)
{
    ets_long buf[10] = {0};
    ets_longArray arr = env_->NewLongArray(5);

    ets_class error_class = env_->FindClass("std/core/ArrayIndexOutOfBoundsException");
    ASSERT_NE(error_class, nullptr);

    {
        env_->SetLongArrayRegion(arr, -1, 2, buf);

        ets_error error = env_->ErrorOccurred();
        ASSERT_NE(error, nullptr);
        env_->ErrorClear();
        ASSERT_EQ(env_->IsInstanceOf(static_cast<ets_object>(error), error_class), ETS_TRUE);
    }
}

TEST_F(EtsNativeInterfaceArrayTest, SetFloatArrayRegionErrorTests)
{
    ets_float buf[10] = {0};
    ets_floatArray arr = env_->NewFloatArray(5);

    ets_class error_class = env_->FindClass("std/core/ArrayIndexOutOfBoundsException");
    ASSERT_NE(error_class, nullptr);

    {
        env_->SetFloatArrayRegion(arr, -1, 2, buf);

        ets_error error = env_->ErrorOccurred();
        ASSERT_NE(error, nullptr);
        env_->ErrorClear();
        ASSERT_EQ(env_->IsInstanceOf(static_cast<ets_object>(error), error_class), ETS_TRUE);
    }
}

TEST_F(EtsNativeInterfaceArrayTest, SetDoubleArrayRegionErrorTests)
{
    ets_double buf[10] = {0};
    ets_doubleArray arr = env_->NewDoubleArray(5);

    ets_class error_class = env_->FindClass("std/core/ArrayIndexOutOfBoundsException");
    ASSERT_NE(error_class, nullptr);

    {
        env_->SetDoubleArrayRegion(arr, -1, 2, buf);

        ets_error error = env_->ErrorOccurred();
        ASSERT_NE(error, nullptr);
        env_->ErrorClear();
        ASSERT_EQ(env_->IsInstanceOf(static_cast<ets_object>(error), error_class), ETS_TRUE);
    }
}

TEST_F(EtsNativeInterfaceArrayTest, GetObjectArrayElementErrorTest)
{
    ets_class cls = env_->FindClass("std/core/String");
    ASSERT_NE(cls, nullptr);
    const std::string example {"sample7"};
    ets_string str = env_->NewStringUTF(example.c_str());
    ASSERT_NE(str, nullptr);
    ets_objectArray array = env_->NewObjectsArray(5, cls, str);
    ASSERT_NE(array, nullptr);

    ets_class error_class = env_->FindClass("std/core/ArrayIndexOutOfBoundsException");
    ASSERT_NE(error_class, nullptr);

    {
        env_->GetObjectArrayElement(array, -1);

        ets_error error = env_->ErrorOccurred();
        ASSERT_NE(error, nullptr);
        env_->ErrorClear();
        ASSERT_EQ(env_->IsInstanceOf(static_cast<ets_object>(error), error_class), ETS_TRUE);
    }
    {
        env_->GetObjectArrayElement(array, 5);

        ets_error error = env_->ErrorOccurred();
        ASSERT_NE(error, nullptr);
        env_->ErrorClear();
        ASSERT_EQ(env_->IsInstanceOf(static_cast<ets_object>(error), error_class), ETS_TRUE);
    }
    {
        env_->GetObjectArrayElement(array, 15);

        ets_error error = env_->ErrorOccurred();
        ASSERT_NE(error, nullptr);
        env_->ErrorClear();
        ASSERT_EQ(env_->IsInstanceOf(static_cast<ets_object>(error), error_class), ETS_TRUE);
    }
}

TEST_F(EtsNativeInterfaceArrayTest, SetObjectArrayElementErrorTests)
{
    ets_class cls = env_->FindClass("std/core/String");
    ASSERT_NE(cls, nullptr);
    const std::string example {"sample7"};
    ets_string str = env_->NewStringUTF(example.c_str());
    ASSERT_NE(str, nullptr);
    ets_objectArray str_array = env_->NewObjectsArray(5, cls, str);
    ASSERT_NE(str_array, nullptr);

    const std::string new_example {"new sample"};
    ets_string new_str = env_->NewStringUTF(new_example.c_str());
    ASSERT_NE(new_str, nullptr);

    ets_class error_class = env_->FindClass("std/core/ArrayIndexOutOfBoundsException");
    ASSERT_NE(error_class, nullptr);

    {
        env_->SetObjectArrayElement(str_array, -1, new_str);

        ets_error error = env_->ErrorOccurred();
        ASSERT_NE(error, nullptr);
        env_->ErrorClear();
        ASSERT_EQ(env_->IsInstanceOf(static_cast<ets_object>(error), error_class), ETS_TRUE);
    }
    {
        env_->SetObjectArrayElement(str_array, 5, new_str);

        ets_error error = env_->ErrorOccurred();
        ASSERT_NE(error, nullptr);
        env_->ErrorClear();
        ASSERT_EQ(env_->IsInstanceOf(static_cast<ets_object>(error), error_class), ETS_TRUE);
    }
}

TEST_F(EtsNativeInterfaceArrayTest, SetObjectArrayElementErrorTests2)
{
    ets_class cls = env_->FindClass("std/core/String");
    ASSERT_NE(cls, nullptr);
    const std::string example {"sample7"};
    ets_string str = env_->NewStringUTF(example.c_str());
    ASSERT_NE(str, nullptr);
    ets_objectArray str_array = env_->NewObjectsArray(5, cls, str);
    ASSERT_NE(str_array, nullptr);

    ets_class int_cls = env_->FindClass("std/core/Int");
    ASSERT_NE(int_cls, nullptr);
    ets_object int_obj = env_->AllocObject(int_cls);
    ASSERT_NE(int_obj, nullptr);

    ets_class error_class = env_->FindClass("std/core/ArrayStoreException");
    ASSERT_NE(error_class, nullptr);

    {
        env_->SetObjectArrayElement(str_array, 1, int_obj);

        ets_error error = env_->ErrorOccurred();
        ASSERT_NE(error, nullptr);
        env_->ErrorClear();
        ASSERT_EQ(env_->IsInstanceOf(static_cast<ets_object>(error), error_class), ETS_TRUE);
    }
}

}  // namespace panda::ets::test

// NOLINTEND(modernize-avoid-c-arrays, readability-magic-numbers)
